package org.dftproject.genesis.core.persistence;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Memento implements IMemento {

	private static final Log log = LogFactory.getLog(Memento.class);

	private Element root;

	public Memento() {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			DOMImplementation impl = builder.getDOMImplementation();
			Document doc = impl.createDocument(null, null, null);
			root = doc.createElement("memento");
			doc.appendChild(root);
		} catch (Exception e) {
			log.error("Failed to create empty memento", e);
		}
	}

	protected Memento(Element root) {
		this.root = root;
	}

	public Memento(InputStream in) throws IOException {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document doc = builder.parse(in);
			root = doc.getDocumentElement();
		} catch (Exception e) {
			log.error("Failed to load memento from stream", e);
		}
	}

	public void store(OutputStream out) throws IOException {
		try {
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.transform(new DOMSource(root.getOwnerDocument()), new StreamResult(out));
		} catch (Exception e) {
			log.error("Failed to store memento to stream", e);
		}
	}

	public IMemento createChild(String type) {
		Element child = root.getOwnerDocument().createElement(type);
		root.appendChild(child);
		return new Memento(child);
	}

	public IMemento getChild(String type) {
		NodeList nodes = root.getChildNodes();
		int size = nodes.getLength();
		for (int i = 0; i < size; i++) {
			Node node = nodes.item(i);
			if (node instanceof Element) {
				Element element = (Element) node;
				if (element.getNodeName().equals(type))
					return new Memento(element);
			}
		}

		return null;
	}

	public IMemento[] getChildren(String type) {
		List<IMemento> children = new LinkedList<IMemento>();
		
		NodeList nodes = root.getChildNodes();
		int size = nodes.getLength();
		for (int i = 0; i < size; i++) {
			Node node = nodes.item(i);
			if (node instanceof Element) {
				Element element = (Element) node;
				if (element.getNodeName().equals(type))
					children.add(new Memento(element));
			}
		}

		return children.toArray(new IMemento[0]);
	}

	public String getString(String key) {
		Attr attr = root.getAttributeNode(key);
        if (attr == null)
			return null;
        return attr.getValue();
	}

	public void putString(String key, String value) {
		if (value == null)
			return;
        root.setAttribute(key, value);
	}

}
